import 'package:meta/meta.dart';

/// A cached value that have an expiry date.
class Cache<T> {
  final String key;

  /// The date after which the value isn't valid anymore.
  final DateTime expiry;

  /// The stored value.
  final T value;

  /// The priority indicates the minimum level to optimize
  /// cleaning operations.
  final CachePriority priority;

  /// Creates a new [Cache] with the given [value] and [expiry] date.
  /// 
  /// A [priority] can also be precised to optimize cleaning
  /// operations.
  Cache(
      {@required this.key,
      @required this.expiry,
      @required this.value,
      this.priority = CachePriority.normal});

  /// Creates a new [Cache] with the given [value] and [expiry] duration from now.
  Cache.fromNow(
      {@required this.key,
      @required Duration expiresIn,
      @required this.value,
      this.priority = CachePriority.normal})
      : this.expiry = DateTime.now().add(expiresIn);

  /// Test if the cache is expired.
  bool isExpired() {
    return DateTime.now().isAfter(this.expiry);
  }

  Cache<T> updated(
      {@required T value, @required Duration expiresIn, CachePriority priority}) {
    return Cache<T>.fromNow(
      key: this.key,
      value: value,
      expiresIn: expiresIn,
      priority: this.priority ?? this.priority,
    );
  }

  Cache<T> invalidate() => Cache<T>(
        key: this.key,
        value: this.value,
        expiry: DateTime.fromMillisecondsSinceEpoch(0),
        priority: this.priority,
      );

    @override
  String toString() {
    final buffer = StringBuffer("Cache<$T> {");
    buffer.write("key: $key, ");
    buffer.write("value: $value, ");
    buffer.write("expiry: $expiry, ");
    buffer.write("priority: $priority ");
    buffer.write("}");
    return buffer.toString();
  }
}

typedef Future<Cache<T>> CacheUpdater<T>(String key);

enum CachePriority {
  low,
  normal,
  high,
  neverRemove,
}
